import { app, shell, BrowserWindow, ipcMain, protocol, session } from 'electron'
import path from 'path'
import fs from 'fs'
import { electronApp, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import Base from './base'
import { resourcePath, assetsDataUrl, convertPath, saveBase64, saveJson, resourcePathStr } from './resources'
import logger from './logger'
import popuMenu from './menu'

logger.info('resourcePathStr: ' + resourcePathStr)

// 注册自定义协议方案为特权协议。
protocol.registerSchemesAsPrivileged([
  {
    scheme: 'wxmaster-resource', // 要注册的自定义协议方案。
    privileges: {
      secure: true, // 将自定义协议视为安全的，类似于https。
      supportFetchAPI: true, // 允许在该协议上使用Fetch API。
      standard: true, // 将使用此协议的URL视为标准的HTTP(S)URL。
      bypassCSP: true, // 允许协议绕过内容安全策略（CSP）检查。
      stream: true, // 启用对响应的流支持。
    },
  },
])

function createWindow(): void {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: Base.winAttr().width,
    height: Base.winAttr().height,
    show: false,
    autoHideMenuBar: true,
    ...(process.platform === 'linux' ? { icon } : {}),
    webPreferences: {
      preload: path.join(__dirname, '../preload/index.js'),
      sandbox: false,
    },
    // frame: false,
    useContentSize: true,
    resizable: false,
  })
  mainWindow.setContentSize(Base.winAttr().width, Base.winAttr().height)
  mainWindow.webContents.openDevTools() // TODO

  mainWindow.on('ready-to-show', () => {
    mainWindow.show()
  })

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url)
    return { action: 'deny' }
  })

  // HMR for renderer base on electron-vite cli.
  // Load the remote URL for development or the local html file for production.
  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
  } else {
    mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'))
  }
  popuMenu(mainWindow) // 右键菜单
  /** 获取窗口参数*/
  ipcMain.handle('win-attr', () => Base.winAttr())
  /** 获取公共资源路径 */
  ipcMain.handle('get-asset', (_event, fn: string) => {
    return resourcePath(fn, '/assets')
  })
  /** 获取DIY卡牌卡图的全路径: diy卡牌卡图路径不能确定文件后缀  */
  ipcMain.handle('get-diy-asset', (_event, author: string, id: string | number) => {
    const files = fs.readdirSync(path.join(resourcePathStr, 'diy/assets', author))
    const fn = files.find((file) => file.startsWith(id + '.'))
    if (fn) {
      return 'wxmaster-resource://' + path.join(resourcePathStr, 'diy/assets', author, fn)
    } else {
      return ''
    }
  })
  /** 获取多个公共资源路径 */
  ipcMain.handle('get-assets', (_event, fns: Map<string, string>) => {
    return fns.entries().reduce(
      (obj, [key, fn]: [string, string]) => {
        obj[key] = resourcePath(fn, '/assets')
        return obj
      },
      {} as Record<string, string>,
    )
  })
  /** 读取JSON文件内容 */
  ipcMain.handle('json-asset', (_event, fn: string) => {
    return JSON.parse(fs.readFileSync(path.join(app.getAppPath(), 'resources', fn), 'utf-8'))
  })
  /** 获取多个资源路径 */
  ipcMain.handle('get-resources', (_event, fns: Map<string, string>) => {
    return fns.entries().reduce(
      (obj, [key, fn]: [string, string]) => {
        obj[key] = resourcePath(fn)
        return obj
      },
      {} as Record<string, string>,
    )
  })
  /** 获取公共资源的DataUrl */
  ipcMain.handle('assets-data-url', (_event, fn: string) => {
    return assetsDataUrl(path.join(app.getAppPath(), 'resources/assets', fn))
  })
  /** 获取字体 */
  ipcMain.handle('get-fonts', (_event, fns: Map<string, string>) => {
    return fns.entries().reduce(
      (obj, [key, fn]: [string, string]) => {
        const data = fs.readFileSync(path.join(app.getAppPath(), 'resources', fn))
        obj[key] = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
        return obj
      },
      {} as Record<string, ArrayBuffer>,
    )
  })
  /** 读取diy作品 */
  ipcMain.handle('diy-portfolio', () => {
    const authors = fs.readdirSync(path.join(resourcePathStr, 'diy/jsons')) // 获取所有作者
    return authors.reduce((map, author) => {
      // 过滤掉以 '_' 开头的文件
      const jsons = fs.readdirSync(path.join(resourcePathStr, 'diy/jsons', author)).filter((fn) => !fn.startsWith('_'))
      const datas = jsons.map((fn) => JSON.parse(fs.readFileSync(path.join(resourcePathStr, 'diy/jsons', author, fn), 'utf-8')))
      map.set(author, datas)
      return map
    }, new Map())
  })
  /** 保存DIY作品 */
  ipcMain.handle('save-portfolio', (_event, data: Wx.Card.Data) => {
    const request = Base.baseResult()
    const picture = data.picture || ''
    data.picture = undefined
    const jsonResult = saveJson(data, path.join(resourcePathStr, 'diy/jsons', data.author!), String(data.id) + '.json')
    if (!jsonResult.success) {
      return jsonResult
    }
    if (picture) {
      const pictureResult = saveBase64(picture, path.join(resourcePathStr, 'diy/assets', data.author!), String(data.id))
      if (!pictureResult.success) {
        fs.unlinkSync(jsonResult.data as string) // 删除之前保存的JSON文件
        return pictureResult
      }
    }
    request.data = `${data.name} 保存成功`
    return request
  })
  /** 删除DIY作品: 重命名*/
  ipcMain.handle('rename-portfolio', (_event, data: Wx.Card.Data) => {
    const request = Base.baseResult()
    const pathstr = path.join(resourcePathStr, 'diy/jsons', data.author!)
    try {
      fs.renameSync(path.join(pathstr, String(data.id) + '.json'), path.join(pathstr, '_' + String(data.id) + '.json'))
      logger.info('rename-portfolio: ' + path.join(pathstr, String(data.id) + '.json'))
    } catch {
      request.success = false
      request.data = '删除失败'
      logger.error('rename-portfolio: ' + path.join(pathstr, String(data.id) + '.json'))
      return request
    }
    return request
  })

  /** 读取DIY Data数据 */
  ipcMain.handle('read-diy-data', () => {
    const request = Base.baseResult()
    const pathstr = path.join(resourcePathStr, 'data/diy.json')
    try {
      const data = JSON.parse(fs.readFileSync(pathstr, 'utf-8'))
      request.data = data
    } catch (error: unknown) {
      request.success = false
      request.msg = (error as Error).toString()
    }
    return request
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  // 清除默认会话的缓存
  session.defaultSession.clearCache().then(() => {
    // console.log('缓存已清除')
  })
  // Set app user model id for windows
  electronApp.setAppUserModelId('com.electron')

  // Default open or close DevTools by F12 in development
  // and ignore CommandOrControl + R in production.
  // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
  // app.on('browser-window-created', (_, window) => {
  //   optimizer.watchWindowShortcuts(window)
  // })

  // IPC test
  // ipcMain.on('ping', () => console.log('pong'))

  createWindow()

  app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })

  // 使用自定义的"wxmaster-resource"协议处理请求。
  protocol.handle('wxmaster-resource', async (request) => {
    // 解码请求URL，去掉协议部分，以获得原始路径。
    // 这里使用正则表达式将"local-resource:/"替换为空字符串，并解码URL编码。
    const decodedUrl = decodeURIComponent(request.url.replace(new RegExp(`^wxmaster-resource:/`, 'i'), ''))

    // 根据操作系统平台，可能需要转换路径格式。
    // 如果是Windows平台，调用convertPath方法转换路径；否则，直接使用解码后的URL。
    const fullPath = process.platform === 'win32' ? convertPath(decodedUrl) : decodedUrl

    // 异步读取文件内容。
    const data = fs.readFileSync(fullPath)

    // 将读取的文件内容封装在Response对象中返回。
    // 这允许Electron应用加载和显示来自自定义协议URL的内容。
    return new Response(data)
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
